package pfc.modeloer;

import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.lang.StringUtils;
import org.apache.ddlutils.Platform;
import org.apache.ddlutils.PlatformFactory;
import org.apache.ddlutils.model.Database;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import pfc.modeloer.ERAttrib;
import pfc.modeloer.ERModel;
import pfc.modeloer.EREntity;
import pfc.utils.Utils;

/**
 *
 * @author E10866343Q
 */
public class ERModelIO {

    public final static String TAG_ERMODEL = "ermodel";
    public final static String TAG_ENTITY = "entity";
    public final static String TAG_ATTRIB = "attrib";
    public final static String TAG_RELATIONSHIP = "relationship";
    public final static String TAG_REFERENCE = "reference";
    String[] erModelProperties = {"name", "idMethod", "version", "databaseName"};
    String[] entityProperties = {"name", "catalog", "schema", "description", "type", "tableName","erElementType","generateIdentity"};
    String[] attribProperties = {"name", "autoIncrement", "javaName", "description", "primaryKey", "required", "typeCode", "type", "size", "scale", "defaultValue", "columnName","generable"};
    String[] erRelationShipProperties = {"name","type"};
    String[] referenceProperties = {"sourceEntityName, targetEntityName, sourceAttribName, targetAttribName"};

    /**
     * @param args the command line arguments
     */
    public void doERModel2XML(ERModel ermodel, String filepath) throws FileNotFoundException {
        PrintStream f = new PrintStream(new FileOutputStream(new File(filepath)));
        f.println(BeanProperties2XML(ermodel, erModelProperties, TAG_ERMODEL));
        for (EREntity e : ermodel.getEntityList()) {
            entity2XML(e, f);
        }
        for (ERRelationShip r : ermodel.getRelationShipList()) {
            relationShip2XML(r, f);
        }
        f.println("</" + TAG_ERMODEL + ">");
        f.flush();
        f.close();
    }

    private void relationShip2XML(ERRelationShip e, PrintStream f) {

//System.out.println("Sacando a XMl la relacion " + e.getName());
        f.println(BeanProperties2XML(e, erRelationShipProperties, TAG_RELATIONSHIP));
        for (ERReference ref:e.getReferences())
        {
            reference2XML(ref,f);

        }
/*
        for (ERAttrib a : e.getAttribs()) {
          //System.out.println("Antes de pasar a XML " + a.toVerboseString());
          attrib2XML(a, f);
        }
  */
        f.println("</" + TAG_RELATIONSHIP + ">");
    }

    private void reference2XML(ERReference r, PrintStream f)
    {
        f.print("<" + TAG_REFERENCE + " sourceEntityName=\"" + r.getSourceEntityName() + "\" sourceAttribName=\"" + r.getSourceAttribName()+"\"");
        f.print(" targetEntityName=\"" + r.getTargetEntityName() + "\" targetAttribName=\"" + r.getTargetAttribName() + "\">");

        //f.println(BeanProperties2XML(r, referenceProperties, TAG_REFERENCE));
        f.println("</" + TAG_REFERENCE + ">");
    }

    private void entity2XML(EREntity e, PrintStream f) {

        f.println(BeanProperties2XML(e, entityProperties, TAG_ENTITY));


        String lista="";
        Iterable<String> l = e.getRepresentativeAttibutes();

        for(Iterator it = l.iterator();it.hasNext();)
        {
           lista += (String)it.next();
           if (it.hasNext())
               lista +=",";
        }

        f.println("<representativeAtts>" + lista + "</representativeAtts>");

        for (ERAttrib a : e.getAttribs()) {
            //System.out.println("Antes de pasar a XML " + a.toVerboseString());
            attrib2XML(a, f);
        }
        f.println("</" + TAG_ENTITY + ">");
    }

    private void attrib2XML(ERAttrib a, PrintStream f) {

        f.println(BeanProperties2XML(a, attribProperties, TAG_ATTRIB));
        f.println("</" + TAG_ATTRIB + ">");
    }

    private void run() throws FileNotFoundException {
        // TODO code application logic here
        BasicDataSource bds = new BasicDataSource();
        bds.setDriverClassName("com.mysql.jdbc.Driver");
        bds.setUrl("jdbc:mysql://localhost/carreras");
        bds.setUsername("root");
        bds.setPassword("root");

        Platform platform = null;
        try {
            platform = PlatformFactory.createNewPlatformInstance(bds);
        } catch (Exception ex) {
            System.out.println(ex);
        }

        String fichero = "modelo.xml";
        Database db = platform.readModelFromDatabase("model");

        ERModel modelo = new ERModel(db);
        doERModel2XML(modelo, fichero);

        ERModel modelo2 = null;
        //System.out.println(modelo2);
        modelo2 = doXML2ERModel(fichero);

        //System.out.println(modelo2);

        print(modelo2);
    }

    private String BeanProperties2XML(Object bean, String[] props, String elemento) {
        String xml = "<" + elemento;

        //System.out.println("Tratando el bean " + bean.getClass().getSimpleName());
        for (String s : props) {
            try {

                String valor = Utils.propertyValueObj(bean, s, PropertyUtils.getProperty(bean, s));
                //System.out.println(bean +" Propiedad: " + s + "= " + valor);
                xml += " " + s + "=\"" + valor + "\"";
                //System.out.println(xml);
            } catch (Exception ex) {
                System.out.println("Vaya por dios, error tratando: " + s + " " + ex);
            }
        }
        xml += ">";

        return xml;
    }

    public ERModel doXML2ERModel(String fichero) {
        ERModel m = new ERModel();

        File f = new File(fichero);
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = null;
        Document doc = null;
        try {
            //dbf.setValidating(true);
            db = dbf.newDocumentBuilder();

            doc = db.parse(f);
        } catch (Exception ex) {
            Logger.getLogger(ERModelIO.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println(ex.getMessage());
        }


        Element root = doc.getDocumentElement();
        //System.out.println("XML root " + root.getAttribute("name"));
        for (String p : erModelProperties) {
            try {
                //System.out.println("Poniendo propiedad "+p+" de ERModel a " + root.getAttribute(p));
                PropertyUtils.setProperty(m, p, root.getAttribute(p));
            } catch (IllegalAccessException ex) {
                System.out.println(ex);
            } catch (InvocationTargetException ex) {
                System.out.println(ex);
            } catch (NoSuchMethodException ex) {
                System.out.println(ex);
            }
        }

        NodeList entities = root.getElementsByTagName("entity");
        for (int i = 0; i < entities.getLength(); i++) {
            Node entity = entities.item(i);
            Element ele = (Element) entity;
            //System.out.println("nodo entity XML = " + ele);
            EREntity e = new EREntity(m);
            for (String p : entityProperties) {
                try {
                    //System.out.println("Poniendo propiedad "+p+" de EREntity a " + ele.getAttribute(p));
                    String value = ele.getAttribute(p);
                    PropertyUtils.setProperty(e, p, Utils.propertyValue(e,p,value));
                } catch (IllegalAccessException ex) {
                    System.out.println(ex);
                } catch (InvocationTargetException ex) {
                    System.out.println(ex);
                } catch (NoSuchMethodException ex) {
                    System.out.println(ex);
                }
            }

            NodeList representativeatts = ele.getElementsByTagName("representativeAtts");
            Node attsN = representativeatts.item(0);
            String atts = attsN.getTextContent();
            String[] vAtt = StringUtils.split(atts, ",");
            for (String s: vAtt)
            {
                e.addRepresentativeAttribute(s);
            }


            NodeList attribs = ele.getElementsByTagName("attrib");
            for (int j = 0; j < attribs.getLength(); j++) {
                Node attrib = attribs.item(j);
                Element e2 = (Element) attrib;
                //System.out.println("nodo attrib XML = " + e2);
                ERAttrib a = new ERAttrib();
                for (String p : attribProperties) {
                    try {
                        //System.out.println("Poniendo propiedad "+p+" de ERAttrib a " + e2.getAttribute(p));
                        PropertyUtils.setProperty(a, p, Utils.propertyValue(a, p, e2.getAttribute(p)));
                    } catch (IllegalAccessException ex) {
                        System.out.println(ex);
                    } catch (InvocationTargetException ex) {
                        System.out.println(ex);
                    } catch (NoSuchMethodException ex) {
                        System.out.println(ex);
                    }
                }

                e.addAttrib(a);
            }

            m.addEntity(e);
        }

        NodeList rels = root.getElementsByTagName("relationship");
        for (int i = 0; i < rels.getLength(); i++) {
            Node rel = rels.item(i);
            Element ele = (Element) rel;
            //System.out.println("nodo entity XML = " + ele);
            ERRelationShip r = RelationShipFactory.createRelationShip(ele);
            for (String p : erRelationShipProperties) {
                try {
                    //System.out.println("Poniendo propiedad "+p+" de " + r  + " a " + ele.getAttribute(p));
                    String valor = ele.getAttribute(p);

                    PropertyUtils.setProperty(r, p, ele.getAttribute(p));
                } catch (IllegalAccessException ex) {
                    System.out.println(ex);
                } catch (InvocationTargetException ex) {
                    System.out.println(ex);
                } catch (NoSuchMethodException ex) {
                    System.out.println(ex);
                }
            }
            NodeList refs = ele.getElementsByTagName("reference");
            for (int j=0; j< refs.getLength();j++) {
                Node ref = refs.item(j);
                Element e = (Element) ref;
                
                String seStr = e.getAttribute("sourceEntityName");
                String teStr = e.getAttribute("targetEntityName");
                String saStr = e.getAttribute("sourceAttribName");
                String taStr = e.getAttribute("targetAttribName");
                /*NamedNodeMap nm = e.getAttributes();
                for (int k=0; k<nm.getLength();k++)
                {
                    System.out.println(nm.item(k).getNodeName() + "=" + nm.item(k).getNodeValue());
                }
                 *
                 */

                EREntity se = m.findEntity(seStr);
                EREntity te = m.findEntity(teStr);
                ERAttrib sa = se.findAttrib(saStr);
                ERAttrib ta = te.findAttrib(taStr);
                ERReference erref = new ERReference(se,te,sa,ta);
                r.addReference(erref);
                r.addMember(se);
                r.addMember(te);
            }


            m.addRelationShip(r);
        }

        for (ERRelationShip rel:m.getRelationShipList())
        {
            for (EREntity e: rel.getMembers())
            {
                if (e.getErElementType()!=ERElement.EntityType.RelationShip)
                    e.addRelationShip(rel);
            }
        }
        
        return m;
    }

    

    private void print(ERModel modelo2) {
        System.out.println(modelo2);
        for (EREntity e : modelo2.getEntityList()) {
            System.out.println("\t" + e);
            for (ERAttrib a : e.getAttribs()) {
                System.out.println("\t\t" + a);
            }
        }
    }

    
}
